package com.oj.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.oj.config.OAuthConfig;
import com.oj.entity.*;
import com.oj.mapper.MessageMapper;
import com.oj.mapper.OAuthMapper;
import com.oj.mapper.PrivilegeMapper;
import com.oj.mapper.UserMapper;
import com.oj.service.UserService;
import com.oj.service.impl.I18nService;
import com.oj.util.*;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestBody;

import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.*;

import static com.oj.util.CodeMsg.*;

@Slf4j
@Controller
public class UserController {

    @Autowired
    private I18nService i18nService;

    @Autowired
    private UserService userService;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OAuthMapper oauthMapper;

    @Autowired
    private MessageMapper messageMapper;

    @Autowired
    private PrivilegeMapper privilegeMapper;

    @ResponseBody
    @RequestMapping(value = {"/api/user/search"}, method = RequestMethod.GET)
    public Result<List> apiUsers(String q, Integer limit, Integer offset){
        if (limit == null) {
            limit = 0;
        }
        if (offset == null) {
            offset = 0;
        }
        List<User> users = userService.search(q, limit, offset);
        for (User user: users) {
            try {
                user.setRegisterDateFriendly(DateUtil.toFriendlyDate(user.getRegdate()) + " " + I18nService.getMessage("profile.join"));
                OnlineUser ou = OnlineUserUtil.getUser(user.getUsername());
                if (ou != null && ou.getOnline() == 1) {
                    user.setLastaccesstime(ou.getLastAccessTime());
                    user.setLastAccessTimeFriendly(DateUtil.toFriendlyDate(ou.getLastAccessTime()));
                    user.setOnline(1);
                    user.setOnlineState(I18nService.getMessage("profile.online"));
                } else {
                    user.setLastAccessTimeFriendly(DateUtil.toFriendlyDate(user.getLastaccesstime()));
                    user.setOnline(0);
                    user.setOnlineState(I18nService.getMessage("profile.Last_visit"));
                }
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
        return Result.success(users);
    }

    @GetMapping(value = {"/problemset/standings",
            "/problemset/standings/page/{pageNum}"})
    public ModelAndView standings(@PathVariable(value = "pageNum",required = false)Integer pageNum,
                                  @RequestParam(value = "pageSize",required = false, defaultValue = "100")Integer pageSize){
        if (pageNum == null) {
            pageNum = 1;
        }
        PageHelper.startPage(pageNum,pageSize);
        Page<User> users = userMapper.findByPaging();
        PageInfo<User> pageInfo = new PageInfo<>(users);
        ModelAndView mv = new ModelAndView();
        mv.addObject("pageInfo", pageInfo);
        mv.setViewName("user/standings.html");
        return mv;
    }

    @GetMapping("/profile/{username}")
    public ModelAndView profile(@PathVariable String username){
        User user = userService.query(username);
        if (user == null) {
            return null;
        }
        user.setLanguageName(OJUtil.getLanguageName("GUET", user.getLanguage()));
        try {
            user.setRegisterDateFriendly(DateUtil.toFriendlyDate(user.getRegdate()));
            OnlineUser ou = OnlineUserUtil.getUser(username);
            if (ou != null && ou.getOnline() == 1) {
                user.setLastaccesstime(ou.getLastAccessTime());
                user.setLastAccessTimeFriendly(DateUtil.toFriendlyDate(ou.getLastAccessTime()));
                user.setOnline(1);
            } else {
                user.setLastAccessTimeFriendly(DateUtil.toFriendlyDate(user.getLastaccesstime()));
                user.setOnline(0);
            }
        } catch (Exception e) {
            // TODO: handle exception
        }

        ModelAndView mv=new ModelAndView();
        mv.addObject("user", user);
        mv.setViewName("user/profile.html");
        return mv;
    }

    @ResponseBody
    @PostMapping(value = "/api/login")
    public Result login(@RequestBody @Validated(value = {User.loginValid.class}) User user,
                        HttpServletRequest request, HttpServletResponse response, Model model){
        try {
            if(userService.login(request, response, user.getUsername(), user.getPassword())){
                String url = request.getSession().getAttribute("session_url").toString();
                if (url == null) {
                    return Result.success("");
                }
                return Result.success(url);
            }
            return Result.error(CodeMsg.USER_NOT_EXIST_OR_PSW_WRONG.locale());
        } catch (Exception e) {
            return Result.error(CodeMsg.INNER_ERROR.locale(0));
        }
    }

    @RequestMapping(value = "/logout", method = RequestMethod.GET)
    public String logout(HttpServletRequest request, HttpServletResponse response, Model model){
        try {
            String username = request.getSession().getAttribute("session_username").toString();
            if (username == null) {
                return "redirect:/";
            }
        } catch (Exception e) {
            // TODO: handle exception
        }

        userService.logout(request, response);
        try {
            String url = request.getSession().getAttribute("session_url").toString();
            return "redirect:" + url;
        } catch (Exception e) {
            return "redirect:/";
        }
    }
    @GetMapping("/registration")
    public String registration(){
        return "user/register";
    }

    @ResponseBody
    @PostMapping(value = "/api/register")
    public Result register(@RequestBody @Validated(value = {User.registerValid.class}) User user,
                           HttpServletRequest request, HttpServletResponse response, Model model){
        List<Map> errMsg = new ArrayList<Map>();
        if (!user.getPassword().equals(user.getRepeat_password())) {
            Map e = new HashMap();
            e.put("repeat_password", "两次输入的密码不一致");
            errMsg.add(e);
            return Result.error(0, errMsg);
        }

        String code = userService.register(request, response, user);
        if("SUCCESS".equals(code)){
            return Result.success(user.getUsername());
        }

        Map e = new HashMap();
        e.put("iner_err", code);
        errMsg.add(e);
        return Result.error(0, errMsg);
    }

    @ResponseBody
    @PostMapping(value = "/api/user-setting")
    public Result userModify(@RequestBody @Validated(value = {User.updateValid.class}) User user,
                           HttpServletRequest request, HttpServletResponse response, Model model){
        log.info(user.toString());
        String username = request.getSession().getAttribute("session_username").toString();
        if (username == null) {
            return Result.error(CodeMsg.USER_NOT_LOGIN.locale());
        }

        User user_ = userService.query(username);
        if (user_ == null) {
            return Result.error(CodeMsg.USER_NOT_LOGIN.locale());
        }

        if (user.getPassword() != null && user.getPassword().length() >0 ) {
            if (!user_.getPassword().equals(user.getOld_password())) {
                return Result.error(CodeMsg.PASSWORD_ERROR.locale());
            }
            if (!user.getPassword().equals(user.getRepeat_password())) {
                return Result.error(CodeMsg.CONFIRM_PASSWORD_ERROR.locale());
            }
            if (user.getPassword().length() < 6 || user.getPassword().length() > 20) {
                return Result.error(CodeMsg.PASSWORD_LEN_ERROR.locale());
            }
        }else {
            user.setPassword(null);
        }

        user.setUsername(username);
        if(userService.update(user)){
            return Result.success(username);
        }

        return Result.error(CodeMsg.INNER_FAULT.locale());
    }

    @ResponseBody
    @PostMapping(value = "/api/ping")
    public Result ping(HttpServletRequest request){
        try {
            String username = request.getSession().getAttribute("session_username").toString();
            if (username != null) {
                log.info(username + " ==> ping");
                OnlineUserUtil.userOnline(username);
            }
        }catch (Exception e) {
        }

        return Result.success();
    }

    @ResponseBody
    @GetMapping(value = "/api/online-users")
    public Result<List> onlineUsers(HttpServletRequest request){
        List<Map> users = new ArrayList<Map>();
        try {
            Iterator<Map.Entry<String, OnlineUser>> it = OnlineUserUtil.getOnlineUsers().entrySet().iterator();
            while(it.hasNext()){
                Map.Entry<String, OnlineUser> entry = it.next();
                String username = entry.getKey();
                if(username == null){
                    continue;
                }
                OnlineUser ou = entry.getValue();
                if (ou.getOnline() == 0) {
                    continue;
                }
                User u = userService.query(username);
                if(u == null){
                    continue;
                }

                Map m = new HashMap();
                m.put("username", username);
                m.put("nickname", (u.getNickname() == null) ? username : u.getNickname());
                m.put("lastAccessTime", DateUtil.toFriendlyDate(ou.getLastAccessTime()));
                m.put("level", u.getRate());
                m.put("levelTitle", i18nService.getMessage("user_rate" + u.getRate()));
                users.add(m);
            }
        } catch (Exception e) {
        }

        return Result.success(users);
    }

    @ResponseBody
    @PostMapping(value = "/api/avatar/modify")
    public Result setAvatar(String url, HttpServletRequest request){
        User user = userService.getUserBySession(request);
        if(user == null){
            return Result.error(0, null);
        }

        user.setAvatar(url);
        userService.update(user);
        request.getSession().setAttribute("session_user_avatar_url", url);
        return Result.success();
    }

    @ResponseBody
    @PostMapping(value = "/api/avatar")
    public Result<List> avatars(HttpServletRequest request){
        User u = userService.getUserBySession(request);
        if(u == null){
            return Result.error(0, null);
        }
        List<Avatar> avatars = new ArrayList<Avatar>();
        ApplicationHome h = new ApplicationHome(getClass());
        File jarF = h.getSource();
        String directory = jarF.getParentFile().toString()+ "/upload";
        String avatar_url_local = directory + "/userphoto/"+ u.getUsername() + "/photo.png";
        File avatar_url_local_file = new File(avatar_url_local);
        if(avatar_url_local_file.exists()){
            Avatar a_local = new Avatar();
            a_local.setProvider("Uploaded photo");
            String avatar_url_local_ = "/upload/userphoto/"+ u.getUsername() + "/photo.png";
            a_local.setAvatar_url(avatar_url_local_);
            if (a_local.getAvatar_url().equals(u.getAvatar())) {
                a_local.setSelected(true);
            }
            avatars.add(a_local);
        }

        Avatar a_gravatar = new Avatar();
        a_gravatar.setProvider("Gravatar");
        a_gravatar.setAvatar_url(AvatarUtil.avatarGenerate(u.getEmail()));
        if (a_gravatar.getAvatar_url().equals(u.getAvatar())) {
            a_gravatar.setSelected(true);
        }
        avatars.add(a_gravatar);

        List<OAuth> oAuthList = oauthMapper.queryByUser(u.getUsername());
        for (OAuth o:oAuthList) {
            Avatar a = new Avatar();
            a.setProvider(o.getProvider());
            a.setAvatar_url(o.getAvatar_url());
            if (a.getAvatar_url().equals(u.getAvatar())) {
                a.setSelected(true);
            }
            avatars.add(a);
        }
        return Result.success(avatars);
    }

    @ResponseBody
    @PostMapping(value = "/api/userInfo")
    public Result userInfo(String username){
        Map userTip = new HashMap();
        User u = new User();
        u = userService.query(username);
        if(u == null){
            return Result.error(0, username);
        }
        u.setRank(userService.getUserRank(u));

        Map param = new HashMap();
        param.put("username", username);
        param.put("limit", 3);
        List<Message> latestMessages = messageMapper.queryList(param);
        for (Message m:latestMessages) {
            String bufString = new String();
            bufString = Html2Text.RemoveHtml(((m.getContent().length()>500)?(m.getContent()).substring(0, 500):m.getContent()));
            m.setContent_abstract(((bufString.length()>100)?(bufString).substring(0, 100)+"...":bufString));
        }

        userTip.put("user", u);
        userTip.put("topic_count", messageMapper.getUserTopicsCount(username));
        userTip.put("topic", latestMessages);
        userTip.put("rank_name", i18nService.getMessage("rank"));
        userTip.put("rating_name", i18nService.getMessage("rating"));
        userTip.put("solved_name", i18nService.getMessage("solved"));
        userTip.put("topic_name", i18nService.getMessage("topic"));
        userTip.put("recent_action_name", i18nService.getMessage("recent-action"));
        userTip.put("no_any_action_name", i18nService.getMessage("no-any-action"));
        return Result.success(userTip);
    }

    @ResponseBody
    @PostMapping(value = "/api/test")
    public Result test(@RequestBody @Validated(value = {User.registerValid.class}) User user, BindingResult result){
        if(result.hasErrors()){
            for (ObjectError err: result.getAllErrors()) {
            }
            return Result.success(result.getFieldError().getDefaultMessage());
        }
        return Result.success(user);
    }

    @GetMapping("/settings")
    public ModelAndView settings(HttpServletRequest request){
        try {
            User user = userService.getUserBySession(request);
            if (user == null) {
                return null;
            }
            Integer bindGitee = 0;
            Integer bindGithub = 0;
            List<OAuth> oAuthList = oauthMapper.queryByUser(user.getUsername());
            for (OAuth o:oAuthList) {
                if (o.getProvider().equals("github")) {
                    bindGithub = 1;
                }
                if (o.getProvider().equals("gitee")) {
                    bindGitee = 1;
                }
            }
            List<Language> langs = OJUtil.getSupportLanguages("GUET");

            ModelAndView mv=new ModelAndView();
            mv.addObject("user", user);
            mv.addObject("languages", langs);
            mv.addObject("oauthList", oAuthList);
            mv.addObject("githubClientID", OAuthConfig.getGithubClientID());
            mv.addObject("giteeClientID", OAuthConfig.getGiteeClientID());
            mv.addObject("giteeRedrectUrl", OAuthConfig.getGiteeRedrectUrl());
            mv.addObject("bindGitee", bindGitee);
            mv.addObject("bindGithub", bindGithub);
            mv.setViewName("user/settings.html");
            return mv;
        }catch (Exception e) {
        }
        return null;
    }

    @ResponseBody
    @PostMapping(value = "/api/avatar/upload")
    public Result<String> uploadAvatar(@RequestParam("photo") MultipartFile photo, HttpServletRequest request){
        try {
            if (photo.isEmpty()){
                return Result.error(0, null);
            }
            String username = request.getSession().getAttribute("session_username").toString();
            if (username == null) {
                return Result.error(0, null);
            }
            User user = userService.query(username);
            if (user == null) {
                return Result.error(0, null);
            }

            ApplicationHome h = new ApplicationHome(getClass());
            File jarF = h.getSource();
            String directory = jarF.getParentFile().toString()+"/upload";
            String path_orig = directory + File.separator + "userphoto"+ File.separator + username;

            File uploadFileSaveDir = new File(path_orig);
            if(!uploadFileSaveDir.exists()){
                uploadFileSaveDir.mkdirs();
            }
            File dest = new File(path_orig + File.separator + "photo.png");

            try {
                photo.transferTo(dest);
            } catch (IOException e) {
                return Result.error(0, null);
            }

            String avatar_url = "/upload/userphoto/" + user.getUsername() + "/photo.png";
            user.setAvatar(avatar_url);
            userService.update(user);
            request.getSession().setAttribute("session_user_avatar_url", avatar_url);
            return Result.success(avatar_url);
        }catch (Exception e) {
            return Result.error(0, null);
        }
    }

    @ResponseBody
    @GetMapping(value = "/api/role/check")
    public Result isAdmin(HttpServletRequest request) {
        User user = userService.getUserBySession(request);
        if (user == null) {
            return Result.error(0, null);
        }

        if (privilegeMapper.query(user.getUsername(), "HEAD") != null) {
            return Result.success();
        }
        if (privilegeMapper.query(user.getUsername(), "ADMIN") != null) {
            return Result.success();
        }

        return Result.error(0, null);
    }

    @GetMapping("/password-recovery")
    public String toPwdRecovery(){
        return "user/password-recovery";
    }


    @ResponseBody
    @PostMapping(value = "/api/password-recovery")
    public Result pwdRecovery(@RequestBody @Validated(value = {User.passRecoveryValid.class}) User user,
                           HttpServletRequest request, HttpServletResponse response, Model model){
        User user_ = userService.query(user.getUsername());
        if(user_ == null || !user_.getEmail().equals(user.getEmail())){
            List<Map> errMsg = new ArrayList<Map>();
            Map e = new HashMap();
            e.put("error", CodeMsg.USERNAME_OR_EMAIL_NOTMATCH.locale().getMsg().toString());
            errMsg.add(e);
            return Result.error(0, errMsg);
        }
        MailUtil mailUtil = new MailUtil();
        mailUtil.sendHtmlMail(user_.getEmail(), "Password Recovery",user_.getUsername() + ", your password is:  " + user_.getPassword() +
                "<br>happyoj.com/enter");
        return Result.success();
    }
}
